1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  package com.google.common.io;
18  
19  import com.google.common.annotations.Beta;
20  import com.google.common.annotations.VisibleForTesting;
21  
22  import java.io.ByteArrayInputStream;
23  import java.io.ByteArrayOutputStream;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.OutputStream;
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  @Beta
41  public final class FileBackedOutputStream extends OutputStream {
42  
43    private final int fileThreshold;
44    private final boolean resetOnFinalize;
45    private final ByteSource source;
46  
47    private OutputStream out;
48    private MemoryOutput memory;
49    private File file;
50  
51    
52    private static class MemoryOutput extends ByteArrayOutputStream {
53      byte[] getBuffer() {
54        return buf;
55      }
56  
57      int getCount() {
58        return count;
59      }
60    }
61  
62    
63    @VisibleForTesting synchronized File getFile() {
64      return file;
65    }
66  
67    
68  
69  
70  
71  
72  
73  
74  
75    public FileBackedOutputStream(int fileThreshold) {
76      this(fileThreshold, false);
77    }
78  
79    
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90    public FileBackedOutputStream(int fileThreshold, boolean resetOnFinalize) {
91      this.fileThreshold = fileThreshold;
92      this.resetOnFinalize = resetOnFinalize;
93      memory = new MemoryOutput();
94      out = memory;
95  
96      if (resetOnFinalize) {
97        source = new ByteSource() {
98          @Override
99          public InputStream openStream() throws IOException {
100           return openInputStream();
101         }
102 
103         @Override protected void finalize() {
104           try {
105             reset();
106           } catch (Throwable t) {
107             t.printStackTrace(System.err);
108           }
109         }
110       };
111     } else {
112       source = new ByteSource() {
113         @Override
114         public InputStream openStream() throws IOException {
115           return openInputStream();
116         }
117       };
118     }
119   }
120 
121   
122 
123 
124 
125 
126 
127   public ByteSource asByteSource() {
128     return source;
129   }
130 
131   private synchronized InputStream openInputStream() throws IOException {
132     if (file != null) {
133       return new FileInputStream(file);
134     } else {
135       return new ByteArrayInputStream(
136           memory.getBuffer(), 0, memory.getCount());
137     }
138   }
139 
140   
141 
142 
143 
144 
145 
146 
147   public synchronized void reset() throws IOException {
148     try {
149       close();
150     } finally {
151       if (memory == null) {
152         memory = new MemoryOutput();
153       } else {
154         memory.reset();
155       }
156       out = memory;
157       if (file != null) {
158         File deleteMe = file;
159         file = null;
160         if (!deleteMe.delete()) {
161           throw new IOException("Could not delete: " + deleteMe);
162         }
163       }
164     }
165   }
166 
167   @Override public synchronized void write(int b) throws IOException {
168     update(1);
169     out.write(b);
170   }
171 
172   @Override public synchronized void write(byte[] b) throws IOException {
173     write(b, 0, b.length);
174   }
175 
176   @Override public synchronized void write(byte[] b, int off, int len)
177       throws IOException {
178     update(len);
179     out.write(b, off, len);
180   }
181 
182   @Override public synchronized void close() throws IOException {
183     out.close();
184   }
185 
186   @Override public synchronized void flush() throws IOException {
187     out.flush();
188   }
189 
190   
191 
192 
193 
194   private void update(int len) throws IOException {
195     if (file == null && (memory.getCount() + len > fileThreshold)) {
196       File temp = File.createTempFile("FileBackedOutputStream", null);
197       if (resetOnFinalize) {
198         
199         
200         temp.deleteOnExit();
201       }
202       FileOutputStream transfer = new FileOutputStream(temp);
203       transfer.write(memory.getBuffer(), 0, memory.getCount());
204       transfer.flush();
205 
206       
207       out = transfer;
208       file = temp;
209       memory = null;
210     }
211   }
212 }